/*
 * 
 *  Este arquivo é parte do software TODO Calculadora. TODO Calculadora é um software livre;
 *  você pode redistribui-lo e/ou modifica-lo dentro dos termos da Licença Pública Geral GNU
 *  como publicada pela Fundação do Software Livre (FSF); na versão 2 da Licença,
 *  ou (na sua opnião) qualquer versão.
 *  
 *	Este programa é distribuido na esperança que possa ser útil, mas SEM NENHUMA GARANTIA;
 *  sem uma garantia implícita de ADEQUAÇÂO a qualquer MERCADO ou APLICAÇÃO EM PARTICULAR.
 *  Veja a Licença Pública Geral GNU para maiores detalhes. Você deve ter recebido uma cópia
 *  da Licença Pública Geral GNU junto com este programa, se não, escreva para a Fundação do
 *	Software Livre(FSF)
 *
 *	Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

package operacoes.basica.tratador;

import interna.tratador.Tratador;
import operacoes.basica.abstrata.AbstractOperacao;
import operacoes.basica.abstrata.AbstractOperacaoBinaria;
import operacoes.basica.abstrata.AbstractOperacaoUnaria;
import operacoes.basica.binaria.Adicao;
import operacoes.basica.binaria.Divisao;
import operacoes.basica.binaria.Multiplicacao;
import operacoes.basica.binaria.Potencia;
import operacoes.basica.binaria.Subtracao;
import operacoes.basica.unaria.Fatorial;
import operacoes.basica.unaria.Logaritmo;
import operacoes.basica.unaria.RaizQuadrada;
import enums.OperacaoBinaria;
import enums.OperacaoUnaria;
import externa.operacao.Operacao;

public class TratadorOperacoes implements Operacao, Tratador {

	private static AbstractOperacao instancia;
	private Adicao adicao;
	private Subtracao subtracao;
	private Multiplicacao multiplicacao;
	private Divisao divisao;

	@Override
	public Number calcula(double elementoUm, double elementoDois,
			OperacaoBinaria operacaoSelecionada) {

		instancia = obtemClasse(operacaoSelecionada);

		if (instancia instanceof AbstractOperacaoBinaria) {
			AbstractOperacaoBinaria operacaoBinaria = (AbstractOperacaoBinaria) instancia;
			operacaoBinaria.executaOperacao(elementoUm, elementoDois);

			instancia = null;

			return operacaoBinaria.getResultado();
		}

		return null;
	}

	/**
	 * Obtem a classe responsável pelo cálculo
	 * 
	 * @param operacao Operação escolhida
	 * @return Instância da classe responsável
	 */
	private AbstractOperacao obtemClasse(OperacaoBinaria operacao) {

		switch (operacao) {
		case ADICAO:
			instancia = new Adicao();
			break;

		case MULTIPLICACAO:
			instancia = new Multiplicacao();
			break;

		case DIVISAO:
			instancia = new Divisao();
			break;

		case POTENCIA:
			instancia = new Potencia();
			break;

		case SUBTRACAO:
			instancia = new Subtracao();
			break;
		}
		return instancia;
	}

	private AbstractOperacao obtemClasse(OperacaoUnaria operacao) {

		switch (operacao) {
		case FATORIAL:
			instancia = new Fatorial();
			break;

		case LOGARITMO:
			instancia = new Logaritmo();
			break;

		case RAIZ_QUADRADA:
			instancia = new RaizQuadrada();
			break;
		}
		return instancia;
	}

	@Override
	public Number calcula(double elemento, OperacaoUnaria operacaoSelecionada) {

		instancia = obtemClasse(operacaoSelecionada);

		if (instancia instanceof AbstractOperacaoUnaria) {
			AbstractOperacaoUnaria operacaoUnaria = (AbstractOperacaoUnaria) instancia;
			operacaoUnaria.executaOperacao(elemento);

			instancia = null;

			return operacaoUnaria.getResultado();
		}

		return null;
	}

	@Override
	public Operacao soma(double elemento) {
		adicao = new Adicao();
		adicao.setElementoUm(elemento);

		return this;
	}

	public Number com(double elemento) {

		Number resultado = null;

		if (adicao != null) {
			adicao.setElementoDois(elemento);
			adicao.executaOperacao(adicao.getElementoUm(), elemento);

			resultado = adicao.getResultado();

			adicao = null;
		} else if (divisao != null) {
			divisao.setElementoDois(elemento);
			divisao.executaOperacao(divisao.getElementoUm(), elemento);

			resultado = divisao.getResultado();

			divisao = null;
		} else if (subtracao != null) {
			subtracao.setElementoDois(elemento);
			subtracao.executaOperacao(subtracao.getElementoUm(), elemento);

			resultado = subtracao.getResultado();

			subtracao = null;
		} else if (multiplicacao != null) {
			multiplicacao.setElementoDois(elemento);
			multiplicacao.executaOperacao(multiplicacao.getElementoUm(),
					elemento);

			resultado = multiplicacao.getResultado();

			multiplicacao = null;
		} else {
			throw new IllegalArgumentException("Falta primeiro operando");
		}

		return resultado;
	}

	@Override
	public Operacao divide(double elmento) {
		divisao = new Divisao();
		divisao.setElementoUm(elmento);

		return this;
	}

	@Override
	public Operacao multiplica(double elmento) {
		multiplicacao = new Multiplicacao();
		multiplicacao.setElementoUm(elmento);

		return this;
	}

	@Override
	public Operacao subtrai(double elmento) {
		subtracao = new Subtracao();
		subtracao.setElementoUm(elmento);

		return this;
	}

	@Override
	public Number raizQuadrada(double elemento) {
		return calcula(elemento, OperacaoUnaria.RAIZ_QUADRADA);
	}
	
	
	

}
